# coding=utf-8

from selenium.common.exceptions import NoSuchElementException
import openpyxl
import csv
import pandas
import chardet

class Public():
    """
    说明：
        提供一些常用功能
        1. readExcel():
            读取Excel单元格数据
        2. writeExcel
            写入Excel单元格数据
        3. readCsv():
            读取Csv单元格数据
        4. writeCsv()
            写入Csv单元格数据
        5. getFileCoding()
            获取文件的编码格式
    """

    def __returnCloumn(self, ws, p_column):
        """
        :param ws: sheet页 type=openpyxl.worksheet.worksheet.Worksheet
        :param p_column: 列名或列数 type=int or str
        :return: 列数转换为字母形式
        """
        if isinstance(p_column, (int)):
                return openpyxl.utils.get_column_letter(p_column) # 数字转换为字母
        elif isinstance(p_column, (str)):
            row_list = list(map(lambda x: x.value, list(ws[1]))) # 解析第一行
            if row_list.count(p_column) == 1: # 相同字段名存在的个数
                for index, cell in enumerate(row_list):
                    if cell == p_column:
                        return openpyxl.utils.get_column_letter(index+1)
            elif row_list.count(p_column) == 0:
                raise ValueError("当前sheet页“%s”不存在字段名为“%s”" % (ws.title, p_column))
            else:
                raise ValueError("当前sheet页“%s”存在多个字段名为“%s”" % (ws.title, p_column))
        else:
            raise TypeError("column参数类型应为int或str类型而不是“%s”，int表示第几列，str表示第一行列字段名" % p_column.__class__)

    def readExcel(self, path, sheet=1, column=1, row=1):
        """
        读取Excel某一单元格数据
        :param path: Excel路径 type=str
        :param sheet: 第几个sheet页或sheet页名称 type=int or str
        :param row: 行数 type=int
        :param column: 列数或列字段名 type=int or str
        :return: 单元格的值
        """
        if not isinstance(row, (int)):
            raise TypeError("row参数类型应为int类型而不是“%s”" % row.__class__)
        wb = openpyxl.load_workbook(path)
        if isinstance(sheet, (str)):
            ws = wb.get_sheet_by_name(sheet)
            column = self.__returnCloumn(ws=ws, p_column=column)
            return ws[column+str(row)].value
        elif isinstance(sheet, (int)):
            ws = wb.get_sheet_by_name(wb.sheetnames[sheet-1])
            column = self.__returnCloumn(ws=ws, p_column=column)
            return ws[column+str(row)].value
        else:
            raise TypeError("sheet参数类型应为int或str类型而不是“%s”，数字表示为第几个sheet页，字符表示sheet页名称" % sheet.__class__)

    def writeExcel(self, path, value="", sheet=1, column=1, row=1):
        """
        写入Excel指定单元格数据
        :param path: Excel路径 type=str
        :param sheet: 第几个sheet页或sheet页名称 type=int or str
        :param row: 行数 type=int
        :param column: 列数或列字段名 type=int or str
        :param value: 需要写入的数据 type=str
        """
        if not isinstance(row, (int)):
            raise TypeError("row参数类型应为int类型而不是“%s”" % row.__class__)
        if not isinstance(value, (str)):
            raise TypeError("value参数类型应为str类型而不是“%s”" % value.__class__)
        wb = openpyxl.load_workbook(path)
        if isinstance(sheet, (str)):
            ws = wb.get_sheet_by_name(sheet)
            column = self.__returnCloumn(ws=ws, p_column=column)
            ws[column+str(row)].value = value
            wb.save(path)
        elif isinstance(sheet, (int)):
            ws = wb.get_sheet_by_name(wb.sheetnames[sheet-1])
            column = self.__returnCloumn(ws=ws, p_column=column)
            ws[column + str(row)].value = value
            wb.save(path)
        else:
            raise TypeError("sheet参数类型应为int或str类型而不是“%s”，数字表示为第几个sheet页，字符表示sheet页名称" % sheet.__class__)

    def readCsv(self, path, column=1, row=1, fields=True):
        """
        说明：读取csv某一单元格数据
        注意：文件编码建议使用utf-8
        :param path: csv文件路径 type=str
        :param row: 行数 type=int
        :param column: 字段名或列数 type=str or int
        :param fields: csv文件第一行是否是字段
        :return: 单元格值
        """
        if not isinstance(row, (int)):
            raise TypeError("row参数类型应为int类型而不是“%s”" % row.__class__)
        with open(path, "r", encoding="utf-8") as csv_file: # 转为list
            csv_reader = csv.reader(csv_file)
            csv_list = list(csv_reader)
        with open(path, "r", encoding="utf-8") as csv_file: # 转为dict
            csv_dict = csv.DictReader(csv_file)
            csv_dict_list = list(csv_dict)
        if fields:
            if isinstance(column, (str)):
                return csv_dict_list[row-2][column]
            elif isinstance(column, (int)):
                return csv_list[row-1][column-1]
            else:
                raise TypeError("csv中有字段时，可以使用字段名表示列名类型为str，当然也可以使用列数表示类型为int，但当前类型为“%s”" % column.__class__)
        else:
            if not isinstance(column, (int)):
                raise TypeError("csv中没有字段，请使用int类型表示列数，当前类型为“%s”" % column.__class__)
            else:
                return csv_list[row - 1][column - 1]

    def writeCsv(self, path, value="", column=1, row=1, fields=True):
        """
        说明：写入csv指定单元格数据
        注意：文件编码建议使用utf-8
        :param path: csv文件路径 type=str
        :param value: 需要写入的数据 type=str
        :param row: 行数 type=int
        :param column: 字段名或列数 type=str or int
        :param fields: csv文件第一行是否是字段
        :return: None
        """
        if not isinstance(row, (int)):
            raise TypeError("row参数类型应为int类型而不是“%s”" % row.__class__)
        if row == 1:
            raise ValueError("第一行为字段行，不允许修改...")
        df = pandas.read_csv(path)
        if fields:
            if isinstance(column, (str)):
                df.at[row-2, column] = value # [行数，列名]
            elif isinstance(column, (int)):
                df.iat[row-2, column-1] = value # [行数，列数]
            else:
                raise TypeError("csv中有字段时，可以使用字段名表示列名类型为str，当然也可以使用列数表示类型为int，但当前类型为“%s”" % column.__class__)
        else:
            if not isinstance(column, (int)):
                raise TypeError("csv中没有字段，请使用int类型表示列数，当前类型为“%s”" % column.__class__)
            else:
                df.iat[row-2, column-1] = value
        df.to_csv(path_or_buf=path, index=False)

    def getFileCoding(self, filepath: str) -> dict:
        """
        说明：
            获取文件的编码格式
        :param filepath: 文件路径 type=str
        :return: {'confidence': 1.0, 'encoding': 'UTF-8'} type=dict
        """
        with open(file=filepath, mode="rb") as f:
            s = f.read()
        return chardet.detect(s)

